#include "jsonmarkuphelper.h"
using namespace FsxWsUtils;

// static
const QString JsonMarkupHelper::Indent = "  ";
const QString JsonMarkupHelper::KmlAltModeAbsolute = "<altitudeMode>absolute</altitudeMode>";
const QString JsonMarkupHelper::KmlDocSchema = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n";
QMap<int, QString> JsonMarkupHelper::_indents = QMap<int, QString>();
KmlSettings JsonMarkupHelper::Settings = KmlSettings();

//
// Default values for struct
//
KmlSettings::KmlSettings() :
    lineStyleColor("ff000000"), lineStyleWidth(1), polyStyleColor("800000ff"),
    waypointModel("PlaneTriangle.dae"), waypointModelBaseUrl("./"), networkLinkUpdatePeriodS(20)
{
    // code
}

//
// Default values for struct
//
KmlSettings::KmlSettings(const QString &lineStyleColor, const int lineStyleWidth,
                         const QString &polyStyleColor, const QString &waypointModel, const QString &waypointModelBaseUrl, const int networkLinkUpdateS) :
    lineStyleColor(lineStyleColor), lineStyleWidth(lineStyleWidth), polyStyleColor(polyStyleColor),
    waypointModel(waypointModel), waypointModelBaseUrl(waypointModelBaseUrl), networkLinkUpdatePeriodS(networkLinkUpdateS)
{
    // code
}

//
// JSON parameter constructor.
//
JsonParameter::JsonParameter() : id(-1), withWaypoints(true), waypointResolutionPeriodMs(-1)
{
    // code goes here
}

//
// JSON parameter constructor.
//
JsonParameter::JsonParameter(int id, bool withWaypoints, qint64 resolutionPeriodMs) :
    id(id), withWaypoints(withWaypoints), waypointResolutionPeriodMs(resolutionPeriodMs)
{
    // code goes here
}

//
// Default kML parameter
//
KmlParameter::KmlParameter() : id(-1), modelUrl(""), networkLinkUpdatePeriodS(-1),
    waypointResolutionPeriodMs(-1), maxNumberWaypoints(-1), waypointTitle(""), waypointFurtherInfo("")
{
    // code goes here
}

//
// KML parameters
//
KmlParameter::KmlParameter(int id, const QString &modelUrl, int networkLinkUpdatePeriodS, qint64 resolutionPeriodMs, int maxNumberWaypoints, const QString &waypointTitle, const QString &waypointFurtherInfo) :
    id(id), modelUrl(modelUrl), networkLinkUpdatePeriodS(networkLinkUpdatePeriodS), maxNumberWaypoints(maxNumberWaypoints),
    waypointResolutionPeriodMs(resolutionPeriodMs), waypointTitle(waypointTitle), waypointFurtherInfo(waypointFurtherInfo)
{
    // code goes here
}

//
// KML parameters
//
KmlParameter::KmlParameter(const KmlParameter &otherParameter) :
    id(otherParameter.id), modelUrl(otherParameter.modelUrl), networkLinkUpdatePeriodS(otherParameter.networkLinkUpdatePeriodS),
    maxNumberWaypoints(otherParameter.maxNumberWaypoints), waypointResolutionPeriodMs(otherParameter.waypointResolutionPeriodMs),
    waypointTitle(otherParameter.waypointTitle), waypointFurtherInfo(otherParameter.waypointFurtherInfo)
{
    // code goes here
}

//
// KML parameters based on settings
//
KmlParameter::KmlParameter(const KmlSettings &kmlSettings) :
    id(-1), modelUrl(kmlSettings.getWaypointModelUrl()), networkLinkUpdatePeriodS(kmlSettings.networkLinkUpdatePeriodS), waypointResolutionPeriodMs(-1),
    maxNumberWaypoints(-1), waypointTitle(""), waypointFurtherInfo("")
{
    // code goes here
}

//
// Indention for level
//
const QString JsonMarkupHelper::Indention(const int level)
{
    if (level < 1) return "";
    if (JsonMarkupHelper::_indents.contains(level)) return JsonMarkupHelper::_indents.value(level);
    QString ind = JsonMarkupHelper::Indent;
    for (int i= 1; i < level; i++) {
        ind = ind.append(JsonMarkupHelper::Indent);
    }
    JsonMarkupHelper::_indents.insert(level, ind);
    return ind;
}

//
// KML representation
//
const QString JsonMarkupHelper::kmlLocation(const int level, const double lat, const double lng, const double altM) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level + 1);
    QString a = ind + "<Location>\n";
    a.append(ind1 + "<latitude>" + QString::number(lat,'f',WPLATLNGPREC)+ "</latitude>\n");
    a.append(ind1 + "<longitude>" + QString::number(lng,'f',WPLATLNGPREC) + "</longitude>\n");
    a.append(ind1 + "<altitude>" + QString::number(altM) + "</altitude>\n");
    a.append(ind + "</Location>\n");
    return a;
}

//
// KML representation
//
const QString JsonMarkupHelper::kmlCoordinates(const int level, const double lat, const double lng, const double altM) {
    const QString ind = JsonMarkupHelper::Indention(level);
    QString a = ind + "<coordinates>" + QString::number(lng, 'f', WPLATLNGPREC)+ "," +
            QString::number(lat,'f', WPLATLNGPREC) + "," +
            QString::number(altM) + "</coordinates>\n";
    return a;
}

//
// KML representation
//
const QString JsonMarkupHelper::kmlLookAt(const int level, const double lat, const double lng, const double altM,
                                          const double headingDeg, const double tiltDeg, const double rollDeg) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level + 1);
    QString a = ind + "<LookAt>\n";
    a.append(ind1 + "<latitude>" + QString::number(lat)+ "</latitude>\n");
    a.append(ind1 + "<longitude>" + QString::number(lng) + "</longitude>\n");
    a.append(ind1 + "<altitude>" + QString::number(altM) + "</altitude>\n");
    a.append(ind1 + "<heading>" + QString::number(headingDeg)+ "</heading>\n");
    a.append(ind1 + "<tilt>" + QString::number(tiltDeg) + "</tilt>\n");
    a.append(ind1 + "<roll>" + QString::number(rollDeg) + "</roll>\n");
    a.append(ind1 + "<altitudeMode>absolute</altitudeMode>\n");
    a.append(ind + "</LookAt>\n");
    return a;
}

//
// KML representation
//
const QString JsonMarkupHelper::kmlStyleMap(const int level, const QString &styleMapId, const QString &styleMapNormal, const QString &styleMapHighlight) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level + 1);
    QString a = ind + "<StyleMap id=\"" + styleMapId + "\">\n";
    a.append(ind1 + "<Pair><key>normal</key><styleUrl>#" + styleMapNormal + "</styleUrl></Pair>\n");
    a.append(ind1 + "<Pair><key>highlight</key><styleUrl>#" + styleMapHighlight + "</styleUrl></Pair>\n");
    a.append(ind + "</StyleMap>\n");
    return a;
}

//
// KML representation
//
const QString JsonMarkupHelper::kmlOrientation(const int level, const double headingDeg, const double tiltDeg, const double rollDeg) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level + 1);
    QString a = ind + "<Orientation>\n";
    a.append(ind1 + "<heading>" + QString::number(headingDeg)+ "</heading>\n");
    a.append(ind1 + "<tilt>" + QString::number(tiltDeg) + "</tilt>\n");
    a.append(ind1 + "<roll>" + QString::number(rollDeg) + "</roll>\n");
    a.append(ind + "</Orientation>\n");
    return a;
}

//
// KML representation
//
const QString JsonMarkupHelper::kmlScale(const int level, const double scaleX, const double scaleY, const double scaleZ) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level + 1);
    QString a = ind + "<Scale>\n";
    a.append(ind1 + "<x>" + QString::number(scaleX)+ "</x>\n");
    a.append(ind1 + "<y>" + QString::number(scaleY) + "</y>\n");
    a.append(ind1 + "<z>" + QString::number(scaleZ) + "</z>\n");
    a.append(ind + "</Scale>\n");
    return a;
}

//
// KML document
//
const QString JsonMarkupHelper::kmlWrapInDocument(const int level, const QString &innerKml, const QString &docName) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level+1);
    QString a = JsonMarkupHelper::KmlDocSchema;
    a.append(ind +"<Document>\n");
    a.append(ind1 + "<name>" + docName + "</name>\n");
    a.append(ind1 + "<open>1</open>\n");
    a.append(innerKml);
    a.append(ind + "</Document>\n");
    a.append(ind + "</kml>\n");
    return a;
}

//
// Combined style
//
const QString JsonMarkupHelper::kmlPlacemarkStyle(const int level, const QString &id, const QString &iconUrl, const double iconScale, const double labelScale) {
    const QString ind = JsonMarkupHelper::Indention(level);
    QString a = ind + "<Style id=\"" + id + "\">\n";
    a.append(JsonMarkupHelper::kmlIconStyle(level+1, "b2ffffff", iconUrl, iconScale));
    a.append(JsonMarkupHelper::kmlLabelStyle(level+1, "b2ffffff", labelScale));
    a.append(ind + "</Style>\n");
    return a;
}

//
// Icon style
//
const QString JsonMarkupHelper::kmlIconStyle(const int level, const QString &colour, const QString &iconUrl, const double iconScale) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level+1);
    QString a = ind  + "<IconStyle>\n";
    a.append(ind1 + "<color>" + colour + "</color>\n");
    a.append(ind1 + "<scale>" + QString::number(iconScale) + "</scale>\n");
    a.append(ind1 + "<Icon><href>" + iconUrl + "</href></Icon>\n");
    a.append(ind1 + "<hotSpot x=\"20\" y=\"2\" xunits=\"pixels\" yunits=\"pixels\"/>\n");
    a.append(ind + "</IconStyle>\n");
    return a;
}

//
// Label style
//
const QString JsonMarkupHelper::kmlLabelStyle(const int level, const QString &colour, const double labelScale) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level+1);
    QString a = ind   + "<LabelStyle>\n";
    a.append(ind1 + "<color>" + colour + "</color>\n");
    a.append(ind1 + "<scale>" + QString::number(labelScale) + "</scale>\n");
    a.append(ind  + "</LabelStyle>\n");
    return a;
}

//
// Line style
//
const QString JsonMarkupHelper::kmlLineStyle(const int level) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level+1);
    QString a = ind   + "<LineStyle>\n";
    a.append(ind1 + "<color>" + Settings.lineStyleColor + "</color>\n");
    a.append(ind1 + "<width>" + QString::number(Settings.lineStyleWidth) + "</width>\n");
    a.append(ind  + "</LineStyle>\n");
    return a;
}

//
// Polystyle
//
const QString JsonMarkupHelper::kmlPolyStyle(const int level) {
    const QString ind = JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level+1);
    QString a = ind   + "<PolyStyle>\n";
    a.append(ind1 + "<color>" + Settings.polyStyleColor + "</color>\n");
    a.append(ind  + "</PolyStyle>\n");
    return a;
}

//
// Network link refresh
// http://stackoverflow.com/questions/6109155/load-a-kml-file-periodically-to-update-position-in-google-earth
//
const QString JsonMarkupHelper::kmlNetworkLinkRefresh(const int level, const QString &url, const int refreshTimeS) {
    if (url.isEmpty()) return "";
    const QString ind =  JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level+1);
    const QString ind2 = JsonMarkupHelper::Indention(level+2);
    int viewRefreshTimeS = refreshTimeS > 10 ? 5 : refreshTimeS / 2;
    QString a = ind   + "<NetworkLink>\n";
    a.append(ind1 + "<Link>\n");
    a.append(ind2 + "<refreshMode>onInterval</refreshMode>\n");
    a.append(ind2 + "<href>" + url + "</href>\n");
    a.append(ind2 + "<refreshInterval>" + QString::number(refreshTimeS) + "</refreshInterval>\n");
    a.append(ind2 + "<viewRefreshMode>onStop</viewRefreshMode>\n");
    a.append(ind2 + "<viewRefreshTime>" + QString::number(viewRefreshTimeS) + "</viewRefreshTime>\n");
    a.append(ind1 + "</Link>\n");
    a.append(ind  + "</NetworkLink>\n");
    return a;
}

//
// Network link control
// https://developers.google.com/kml/documentation/kmlreference#networklinkcontrol
//
const QString JsonMarkupHelper::kmlNetworkLinkControl(const int level, const int refreshOffsetMs, const QString &lookAtOrCamera) {
    const QString ind =  JsonMarkupHelper::Indention(level);
    const QString ind1 = JsonMarkupHelper::Indention(level+1);
    QDateTime dt = QDateTime::currentDateTime();
    dt.addMSecs((qint64)(refreshOffsetMs * 1000));
    QString a = ind   + "<NetworkLinkControl>\n";
    a.append(ind1 + "<minRefreshPeriod>5</minRefreshPeriod>\n");
    a.append(ind1 + "<expires>" + JsonMarkupHelper::kmlDateTime(dt.currentMSecsSinceEpoch(), false) + "</expires>\n");
    if (!lookAtOrCamera.isEmpty()) a.append(lookAtOrCamera);
    a.append(ind  + "</NetworkLinkControl>\n");
    return a;
}

//
// Wrap JSON code in Js function for JSONP
//
const QString JsonMarkupHelper::jsonWrapInJsFunctionCall(const int level, const QString &functionName, const QString &innerJson)
{
    // see http://www.west-wind.com/weblog/posts/2007/Jul/04/JSONP-for-crosssite-Callbacks
    const QString ind =  JsonMarkupHelper::Indention(level);
    QString a = ind + functionName + "(";
    a.append(innerJson);
    a.append(");");
    return a;
}

//
// Kml date time: https://developers.google.com/kml/documentation/kmlreference#timestamp
//
const QString JsonMarkupHelper::kmlDateTime(const qint64 timestamp, const bool utc) {
    QDateTime dt;
    if (timestamp >0)
        dt.setMSecsSinceEpoch(timestamp);
    else
        dt = utc ? QDateTime::currentDateTimeUtc() : QDateTime::currentDateTime();
    QString kmlDateTime = dt.toString("yyyy-mm-ddThh:mm:ss");
    return kmlDateTime + (utc ? "Z" : Utils::utcDifferenceHourMin());
}

//
// Refreshing network link document
//
const QString JsonMarkupHelper::kmlNetworkLinkDocument(const QString &url, const QString &networkLinkName, const int refreshTimeS) {
    QString networkLink = JsonMarkupHelper::kmlNetworkLinkRefresh(1, url, refreshTimeS);
    networkLink = JsonMarkupHelper::kmlWrapInDocument(0, networkLink, networkLinkName);
    return networkLink;
}
